//
// Copyright 2013-2015 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: GPL-3.0-or-later
//

#pragma once

#include <uhd/config.hpp>
#include <uhd/transport/nirio/status.h>
#include <stddef.h>
#include <stdint.h>
#include <string>
#if defined(UHD_PLATFORM_WIN32)
#    include <windows.h>
#    ifdef _MSC_VER
#        pragma warning( \
            disable : 4201) // nonstandard extension used : nameless struct/union
#    endif
#    include <winioctl.h>
#    ifdef _MSC_VER
#        pragma warning(default : 4201)
#    endif
#elif defined(UHD_PLATFORM_MACOS)
#    include <IOKit/IOKitLib.h>
#elif defined(UHD_PLATFORM_LINUX)
#    include <linux/ioctl.h>
#endif

#if __GNUC__
typedef uint64_t aligned_uint64_t __attribute__((aligned(8)));
#else
typedef uint64_t aligned_uint64_t;
#endif

// IOCTL Definitions

#if defined(UHD_PLATFORM_WIN32)

#    define IOCTL_ACCESS_ANY   (FILE_ANY_ACCESS)
#    define IOCTL_ACCESS_READ  (FILE_READ_ACCESS)
#    define IOCTL_ACCESS_WRITE (FILE_WRITE_ACCESS)
#    define IOCTL_ACCESS_RW    (FILE_READ_ACCESS | FILE_WRITE_ACCESS)

#    define IOCTL(type, function, access) \
        CTL_CODE((0x8000 + type), (0x800 + function), METHOD_BUFFERED, access)

#elif defined(UHD_PLATFORM_MACOS)

#    define IOCTL_ACCESS_ANY   (0U)
#    define IOCTL_ACCESS_READ  (1U)
#    define IOCTL_ACCESS_WRITE (2U)
#    define IOCTL_ACCESS_RW    (3U)

#    define IOCTL(type, function, access) \
        (((access & 0x0003) << 30) | ((type & 0x00FF) << 16) | ((function & 0xFFFF) << 0))

#elif defined(UHD_PLATFORM_LINUX)

#    define IOCTL_ACCESS_ANY   (_IOC_NONE)
#    define IOCTL_ACCESS_READ  (_IOC_READ)
#    define IOCTL_ACCESS_WRITE (_IOC_WRITE)
#    define IOCTL_ACCESS_RW    (_IOC_READ | _IOC_WRITE)

struct nirio_ioctl_block_t
{
    aligned_uint64_t in_buf;
    aligned_uint64_t out_buf;
    uint32_t in_buf_len;
    uint32_t out_buf_len;
    uint32_t bytes_returned;
    uint32_t padding;
};

#    define IOCTL(type, function, access) \
        _IOC(access, type, function, sizeof(nirio_ioctl_block_t))

#else

#    define IOCTL_ACCESS_ANY   (0U)
#    define IOCTL_ACCESS_READ  (1U)
#    define IOCTL_ACCESS_WRITE (2U)
#    define IOCTL_ACCESS_RW    (3U)

#    define IOCTL(type, function, access) \
        (((access & 0x0003) << 30) | ((type & 0x00FF) << 16) | ((function & 0xFFFF) << 0))

#endif

namespace nirio_driver_iface {

// Device handle definition
#if defined(UHD_PLATFORM_LINUX)
typedef int rio_dev_handle_t;
#elif defined(UHD_PLATFORM_WIN32)
typedef HANDLE rio_dev_handle_t;
#elif defined(UHD_PLATFORM_MACOS)
typedef io_connect_t rio_dev_handle_t;
#else
typedef int rio_dev_handle_t;
#endif
static const rio_dev_handle_t INVALID_RIO_HANDLE = ((rio_dev_handle_t)-1);

// Memory mapping container definition
#if defined(UHD_PLATFORM_LINUX)
struct rio_mmap_t
{
    rio_mmap_t() : addr(NULL), size(0) {}
    void* addr;
    size_t size;

    bool is_null()
    {
        return (size == 0 || addr == NULL);
    }
};
#elif defined(UHD_PLATFORM_WIN32)
enum access_mode_t { ACCESS_MODE_READ, ACCESS_MODE_WRITE };

struct rio_mmap_params_t
{
    uint64_t mapped_va_ptr;
    uint64_t map_ready_event_handle;
    uint32_t size;
    uint16_t memoryType;
    uint8_t access_mode;
};

struct rio_mmap_threadargs_t
{
    rio_dev_handle_t device_handle;
    rio_mmap_params_t params;
    nirio_status status;
};

struct rio_mmap_t
{
    rio_mmap_t() : addr(NULL) {}
    void* addr;
    HANDLE map_thread_handle;
    rio_mmap_threadargs_t map_thread_args;

    bool is_null()
    {
        return addr == NULL;
    }
};
#else
struct rio_mmap_t
{
    rio_mmap_t() : addr(NULL) {}
    void* addr;

    bool is_null()
    {
        return addr == NULL;
    }
};
#endif

nirio_status rio_open(const std::string& device_path, rio_dev_handle_t& device_handle);

void rio_close(rio_dev_handle_t& device_handle);

bool rio_isopen(rio_dev_handle_t device_handle);

nirio_status rio_ioctl(rio_dev_handle_t device_handle,
    uint32_t ioctl_code,
    const void* write_buf,
    size_t write_buf_len,
    void* read_buf,
    size_t read_buf_len);

nirio_status rio_mmap(rio_dev_handle_t device_handle,
    uint16_t memory_type,
    size_t size,
    bool writable,
    rio_mmap_t& map);

nirio_status rio_munmap(rio_mmap_t& map);
} // namespace nirio_driver_iface
